package com.junzhiit.nengji.app.web.util.wechat;

import java.io.BufferedReader;
import java.io.ByteArrayInputStream;
import java.io.File;
import java.io.FileInputStream;
import java.io.FileOutputStream;
import java.io.IOException;
import java.io.InputStream;
import java.io.InputStreamReader;
import java.io.OutputStream;
import java.io.UnsupportedEncodingException;
import java.net.HttpURLConnection;
import java.net.URL;
import java.security.KeyStore;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.Date;
import java.util.HashMap;
import java.util.Iterator;
import java.util.List;
import java.util.Map;
import java.util.Map.Entry;
import java.util.Set;
import java.util.SortedMap;
import java.util.TreeMap;

import javax.net.ssl.SSLContext;

import org.apache.commons.lang3.StringUtils;
import org.apache.commons.logging.Log;
import org.apache.commons.logging.LogFactory;
import org.apache.http.HttpEntity;
import org.apache.http.HttpResponse;
import org.apache.http.client.ClientProtocolException;
import org.apache.http.client.methods.CloseableHttpResponse;
import org.apache.http.client.methods.HttpPost;
import org.apache.http.conn.ssl.SSLConnectionSocketFactory;
import org.apache.http.conn.ssl.SSLContexts;
import org.apache.http.entity.StringEntity;
import org.apache.http.impl.client.CloseableHttpClient;
import org.apache.http.impl.client.HttpClientBuilder;
import org.apache.http.impl.client.HttpClients;
import org.apache.http.protocol.HTTP;
import org.apache.http.util.EntityUtils;
import org.dom4j.Document;
import org.dom4j.Element;
import org.dom4j.io.SAXReader;

import com.bainaeco.framework.util.DBKey;
import com.bainaeco.framework.util.DateUtil;
import com.bainaeco.framework.util.HttpClientUtils;
import com.bainaeco.framework.util.JsonUtil;
import com.bainaeco.framework.util.PropertiesUtil;
import com.bainaeco.framework.util.SystemCodeStatus;
import com.junzhiit.nengji.app.web.util.Constants;
import com.junzhiit.nengji.app.web.util.aliyun.AliyunOSSClientUtil;
import com.junzhiit.nengji.app.web.util.rsa.Coder;
import com.junzhiit.nengji.app.web.util.wechat.bean.AccessToken;
import com.junzhiit.nengji.app.web.util.wechat.bean.RefundOrder;
import com.junzhiit.nengji.app.web.util.wechat.bean.RefundResult;
import com.junzhiit.nengji.model.WechatConfig;

public class WeChatUtils {

	private static Log logger = LogFactory.getLog(WeChatUtils.class);

	private static AccessToken accessToken;

	private static String appid;

	private static String secret;

	private static String partner;

	private static String token;

	private static String encodingAESKey;

	private static String SIGN_TYPE = "MD5";

	private static String scene;

	private static String DEFAULT_SCENE = "localhost";

	private static String urlNotify;

	static {
		// 初始化微信配置
		logger.info("WeChat init start...");
		try {
			// scene = PropertiesUtil.getProperties(Constants.WEB_CONFIGURE,
			// Constants.WECHAT_FOR_SCENE);
			appid = PropertiesUtil.getProperties(Constants.KEY_PROPERTIES_FOR_SYSTEM,
					getScene() + "." + Constants.WECHAT_KEY_FOR_APPID);
			secret = PropertiesUtil.getProperties(Constants.KEY_PROPERTIES_FOR_SYSTEM,
					getScene() + "." + Constants.WECHAT_KEY_FOR_SECRET);
			partner = PropertiesUtil.getProperties(Constants.KEY_PROPERTIES_FOR_SYSTEM,
					getScene() + "." + Constants.WECHAT_KEY_FOR_PARTNER);
			token = PropertiesUtil.getProperties(Constants.KEY_PROPERTIES_FOR_SYSTEM,
					getScene() + "." + Constants.WECHAT_KEY_FOR_TOKEN);
			encodingAESKey = PropertiesUtil.getProperties(Constants.KEY_PROPERTIES_FOR_SYSTEM,
					getScene() + "." + Constants.WECHAT_KEY_FOR_ENCODING);
			urlNotify = PropertiesUtil.getProperties(Constants.KEY_PROPERTIES_FOR_SYSTEM,
					getScene() + "." + Constants.WECHAT_KEY_FOR_URL_NOTIFY);

			logger.info("scene=>" + scene);
			logger.info("appid=>" + appid);
			logger.info("secret=>" + secret);
			logger.info("partner=>" + partner);
			logger.info("token=>" + token);
			logger.info("encodingAESKey=>" + encodingAESKey);
			logger.info("urlNotify=>" + urlNotify);

			logger.info("init secret completed.");
		} catch (IOException e) {
			e.printStackTrace();
		}
		logger.info("WeChat init end...");
	}

	public static String getAppId() {
		if (appid == null) {
			try {
				appid = PropertiesUtil.getProperties(Constants.KEY_PROPERTIES_FOR_SYSTEM,
						getScene() + "." + Constants.WECHAT_KEY_FOR_APPID);
			} catch (Exception e) {
				e.printStackTrace();
			}
		}
		return appid;
	}

	public static String getSecret() {
		if (secret == null) {
			try {
				secret = PropertiesUtil.getProperties(Constants.KEY_PROPERTIES_FOR_SYSTEM,
						getScene() + "." + Constants.WECHAT_KEY_FOR_SECRET);
			} catch (Exception e) {
				e.printStackTrace();
			}
		}
		return secret;
	}

	private static String getScene() {
		if (StringUtils.isBlank(scene)) {
			scene = DEFAULT_SCENE;
		}
		return scene;
	}

	public static String getKeyFullName(String name) {
		StringBuilder prefix = new StringBuilder();
		try {
			String code = PropertiesUtil.getProperties(Constants.WEB_CONFIGURE, Constants.WECHAT_FOR_CODE);
			prefix.append(scene).append("_").append(code).append("_").append(name);
		} catch (IOException e) {
			e.printStackTrace();
		}
		return prefix.toString();
	}

	private static String getKeyForPKCS12Path(String scene) {
		return Constants.KEY_PKCS12_FOR_WECHAT.replace("{scene}", scene);
	}

	public static AccessToken getAccessToken() {
		if (accessToken == null || (accessToken.getErrcode() == Config.CODE_STATUS_SUCCESS
				&& accessToken.getExpiresTime().getTime() < new Date().getTime())) {
			StringBuilder url = new StringBuilder(Config.URL_ACCESS_TOKEN);
			url.append("&appid=").append(Constants.APPID).append("&secret=").append(Constants.APPSECRET);
			try {
				accessToken = new AccessToken();
				String content = HttpClientUtils.getMethodRequest(url.toString());
				Map<String, Object> map = JsonUtil.toMap(content);
				if (map.get("errcode") == null) {
					accessToken.setAccessToken((String) map.get("access_token"));
					int expiresIn = (Integer) map.get("expires_in");
					accessToken.setExpiresTime(DateUtil.getDateByHour(new Date(), expiresIn / 60 / 60));
				} else {
					accessToken.setErrcode((Integer) map.get("errcode"));
					accessToken.setErrmsg((String) map.get("errmsg"));
				}
			} catch (Exception e) {
				accessToken.setErrcode(SystemCodeStatus.REQUEST_TIMEOUT);
				accessToken.setErrmsg("connect timed out...");
				e.printStackTrace();
			}
			logger.info("access_token:" + accessToken.getAccessToken());
		}
		return accessToken;
	}
	
	public static AccessToken getAccessToken(WechatConfig wechatConfig) {
		if (accessToken == null || (accessToken.getErrcode() == Config.CODE_STATUS_SUCCESS
				&& accessToken.getExpiresTime().getTime() < new Date().getTime())) {
			StringBuilder url = new StringBuilder(Config.URL_ACCESS_TOKEN);
			url.append("&appid=").append(wechatConfig.getAppId()).append("&secret=").append(wechatConfig.getSecret());
			try {
				accessToken = new AccessToken();
				String content = HttpClientUtils.getMethodRequest(url.toString());
				Map<String, Object> map = JsonUtil.toMap(content);
				if (map.get("errcode") == null) {
					accessToken.setAccessToken((String) map.get("access_token"));
					int expiresIn = (Integer) map.get("expires_in");
					accessToken.setExpiresTime(DateUtil.getDateByHour(new Date(), expiresIn / 60 / 60));
				} else {
					accessToken.setErrcode((Integer) map.get("errcode"));
					accessToken.setErrmsg((String) map.get("errmsg"));
				}
			} catch (Exception e) {
				accessToken.setErrcode(SystemCodeStatus.REQUEST_TIMEOUT);
				accessToken.setErrmsg("connect timed out...");
				e.printStackTrace();
			}
			logger.info("access_token:" + accessToken.getAccessToken());
		}
		return accessToken;
	}

	@SuppressWarnings("unchecked")
	public static List<String> getOpenIds() {
		List<String> openIds = new ArrayList<String>();
		AccessToken accessToken = getAccessToken();
		StringBuilder url = new StringBuilder(Config.URL_GET_USER_LIST);
		url.append("?access_token=").append(accessToken.getAccessToken());
		try {
			String content = HttpClientUtils.getMethodRequest(url.toString());
			Map<String, Object> map = JsonUtil.toMap(content);
			Map<String, Object> openIdMap = (Map<String, Object>) map.get("data");
			openIds.addAll((List<String>) openIdMap.get("openid"));
			int total = (Integer) map.get("total");
			int count = (Integer) map.get("count");
			int page = total / count;
			if (total % count > 0)
				page += 1;
			String nextOpenId = (String) map.get("next_openid");
			for (int i = 1; i < page; i++) {
				content = HttpClientUtils.getMethodRequest(url.toString() + "&next_openid=" + nextOpenId);
				map = JsonUtil.toMap(content);
				openIdMap = (Map<String, Object>) map.get("data");
				openIds.addAll((List<String>) openIdMap.get("openid"));
				nextOpenId = (String) map.get("next_openid");
			}
			logger.info(openIds.size());
		} catch (Exception e) {
			e.printStackTrace();
		}
		return openIds;
	}

	private static int getLanguage(String lang) {
		int key = 0;
		if (lang != null && lang.equals("zh_TW"))
			key = 1;
		else if (lang != null && lang.equals("en"))
			key = 2;
		return key;
	}

	public static String getRequestXml(SortedMap<Object, Object> parameters) {
		StringBuffer sb = new StringBuffer();
		sb.append("<xml>");
		Set<Entry<Object, Object>> es = parameters.entrySet();
		Iterator<Entry<Object, Object>> it = es.iterator();
		while (it.hasNext()) {
			Map.Entry<Object, Object> entry = (Map.Entry<Object, Object>) it.next();
			String k = (String) entry.getKey();
			String v = (String) entry.getValue();
			if ("attach".equalsIgnoreCase(k) || "body".equalsIgnoreCase(k) || "sign".equalsIgnoreCase(k)) {
				sb.append("<" + k + ">" + "<![CDATA[" + v + "]]></" + k + ">");
			} else {
				sb.append("<" + k + ">" + v + "</" + k + ">");
			}
		}
		sb.append("</xml>");
		return sb.toString();
	}
	
	public static RefundResult refund(RefundOrder refundOrder){
		RefundResult refundResult = new RefundResult();
		try {
			String nonceStr = DBKey.getUUID();
			String[] array = new String[] {
					"appid=" + appid,
					"mch_id=" + partner,
					"nonce_str=" + nonceStr,
					"transaction_id=" + refundOrder.getTransactionId(),
					"out_refund_no=" + refundOrder.getOutRefundNo(),
					"total_fee=" + String.valueOf(refundOrder.getTotalFee()),
					"refund_fee=" + String.valueOf(refundOrder.getRefundFee()),
					"refund_desc=" + refundOrder.getRefundDesc()
			};
			Arrays.sort(array);
			StringBuffer sb = new StringBuffer();
			for(int i = 0,len = array.length;i < len; i++){
				if(i !=0 ){
					sb.append("&");
				}
	            sb.append(array[i]);
	        }
			sb.append("&key=").append(secret);
			logger.info("StringA: " + sb.toString());
			String sign = Coder.encodeByMD5(sb.toString()).toUpperCase();
			logger.info("sign: " + sign);
			SortedMap<Object,Object> parameters = new TreeMap<Object,Object>();
			parameters.put("appid", appid);
			parameters.put("mch_id", partner);
			parameters.put("nonce_str", nonceStr);
			parameters.put("sign", sign);
			parameters.put("transaction_id", refundOrder.getTransactionId());
			parameters.put("out_refund_no", refundOrder.getOutRefundNo());
			parameters.put("total_fee", String.valueOf(refundOrder.getTotalFee()));
			parameters.put("refund_fee", String.valueOf(refundOrder.getRefundFee()));
			parameters.put("refund_desc", refundOrder.getRefundDesc());
			String xml = getRequestXml(parameters);
			logger.info("sign: " + xml);
			String content = SSLRequest(Config.URL_PAY_REFUND, xml);
			logger.info(content);
			Map<String, String> map = parseXml(content);
			if(map.get("return_code").equals("SUCCESS")){
				refundResult.setOutRefundNo(map.get("out_refund_no"));
				refundResult.setTransactionId(map.get("transaction_id"));
				refundResult.setRefundId(map.get("refund_id"));
				refundResult.setOutTradeNo(map.get("out_trade_no"));
				refundResult.setTotalFee(Long.valueOf(map.get("total_fee")));
				refundResult.setRefundFee(Long.valueOf(map.get("refund_fee")));
				if(map.get("settlement_refund_fee")!=null){
					refundResult.setSettlementRefundFee(Long.valueOf(map.get("settlement_refund_fee")));
					refundResult.setSettlementTotalFee(Long.valueOf(map.get("settlement_total_fee")));	
				}
			}else{
				logger.error(content);
			}
		} catch (Exception e) {
			logger.error(e.getMessage(),e);
		}
		return refundResult;
	}

	@SuppressWarnings("unchecked")
	public static Map<String, String> parseXml(String message)
			throws Exception {
		// 将解析结果存储在HashMap中
		Map<String, String> map = new HashMap<String, String>();

		// 从request中取得输入流
		InputStream inputStream = new ByteArrayInputStream(message.getBytes("UTF-8"));
		
		// 读取输入流
		SAXReader reader = new SAXReader();
		Document document = reader.read(inputStream);
		// 得到xml根元素
		Element root = document.getRootElement();
		// 得到根元素的所有子节点
		List<Element> elementList = root.elements();

		// 遍历所有子节点
		for (Element e : elementList)
			map.put(e.getName(), e.getText());

		// 释放资源
		inputStream.close();
		inputStream = null;
		return map;
	}
	
	public static String jsonPostMethodRequest(String action, String josn) {
		String result = null;
		try {
			URL url = new URL(action);
			HttpURLConnection http = (HttpURLConnection) url.openConnection();
			http.setRequestMethod("POST");
			http.setRequestProperty("Content-Type", "application/x-www-form-urlencoded");
			http.setDoOutput(true);
			http.setDoInput(true);
			System.setProperty("sun.net.client.defaultConnectTimeout", "30000");// 连接超时30秒
			System.setProperty("sun.net.client.defaultReadTimeout", "30000"); // 读取超时30秒
			http.connect();
			OutputStream os = http.getOutputStream();
			os.write(josn.getBytes("UTF-8"));// 传入参数
			os.flush();
			os.close();

			InputStream is = http.getInputStream();
			int size = is.available();
			byte[] jsonBytes = new byte[size];
			is.read(jsonBytes);
			result = new String(jsonBytes, "UTF-8");
			logger.info(result);
		} catch (Exception e) {
			e.printStackTrace();
		}
		return result;
	}

	public static String SSLRequest(String action, String data) {
		StringBuffer result = new StringBuffer();
		try {
			KeyStore keyStore = KeyStore.getInstance("PKCS12");
			String certFilePath = getKeyForPKCS12Path(scene);
			FileInputStream instream = new FileInputStream(new File(certFilePath));
			keyStore.load(instream, partner.toCharArray());
			SSLContext sslcontext = SSLContexts.custom().loadKeyMaterial(keyStore, partner.toCharArray()).build();
			SSLConnectionSocketFactory sslsf = new SSLConnectionSocketFactory(sslcontext, new String[] { "TLSv1" },
					null, SSLConnectionSocketFactory.BROWSER_COMPATIBLE_HOSTNAME_VERIFIER);
			CloseableHttpClient httpclient = HttpClients.custom().setSSLSocketFactory(sslsf).build();
			HttpPost httpost = new HttpPost(action);
			httpost.addHeader("Connection", "keep-alive");
			httpost.addHeader("Accept", "*/*");
			httpost.addHeader("Content-Type", "application/x-www-form-urlencoded; charset=UTF-8");
			httpost.addHeader("Host", "api.mch.weixin.qq.com");
			httpost.addHeader("X-Requested-With", "XMLHttpRequest");
			httpost.addHeader("Cache-Control", "max-age=0");
			httpost.addHeader("User-Agent", "Mozilla/4.0 (compatible; MSIE 8.0; Windows NT 6.0) ");
			httpost.setEntity(new StringEntity(data, "UTF-8"));
			System.out.println("executing request" + httpost.getRequestLine());
			CloseableHttpResponse response = httpclient.execute(httpost);
			try {
				HttpEntity entity = response.getEntity();
				if (entity != null) {
					System.out.println("Response content length: " + entity.getContentLength());
					BufferedReader bufferedReader = new BufferedReader(
							new InputStreamReader(entity.getContent(), "UTF-8"));
					String text;
					while ((text = bufferedReader.readLine()) != null) {
						result.append(text);
					}
				}
				EntityUtils.consume(entity);
			} catch (IOException e) {
				e.printStackTrace();
			} finally {
				response.close();
			}
		} catch (Exception e) {
			e.printStackTrace();
		}
		return result.toString();
	}

	public static void sendCustomMessage(String openId, String content) {
		StringBuilder url = new StringBuilder(Config.URL_MESSAGE_CUSTOM_SEND);
		url.append("?access_token=").append(getAccessToken().getAccessToken());
		Map<String, Object> map = new HashMap<String, Object>();
		Map<String, Object> contentMap = new HashMap<String, Object>();
		contentMap.put("content", content);
		map.put("touser", openId);
		map.put("msgtype", "text");
		map.put("text", contentMap);
		String json = JsonUtil.toJson(map);
		jsonPostMethodRequest(url.toString(), json);
	}
	
	public static void wxQRCode(String backetName,String code,String shopId,String path,String name,String url){
		AccessToken accessToken = getAccessToken();
		Map<String, Object> params = new HashMap<>();
		/*if(StringUtils.isNoneBlank(code)){
			params.put("scene", code);//参数
			params.put("scene", shopId);
		}*/
		params.put("path", url+"?shopId="+shopId);//路径
		params.put("width", 430);
		CloseableHttpClient  httpClient = HttpClientBuilder.create().build();
        HttpPost httpPost = new HttpPost(Constants.URL_QRCODE_CREATE+accessToken.getAccessToken());  // 接口
        httpPost.addHeader(HTTP.CONTENT_TYPE, "application/json");
        String body = JsonUtil.toJson(params); //必须是json模式的 post   
        StringEntity entity;
        try {
			entity = new StringEntity(body);
			entity.setContentType("image/png");
	        httpPost.setEntity(entity);
	        HttpResponse response;

	        response = httpClient.execute(httpPost);
	        InputStream inputStream = response.getEntity().getContent();
	        //saveToImgByInputStream(inputStream,path,name);  //保存图片
	        AliyunOSSClientUtil.uploadObject2OSS(inputStream, name, backetName, path);
		} catch (UnsupportedEncodingException e) {
			e.printStackTrace();
		} catch (ClientProtocolException e) {
			e.printStackTrace();
		} catch (IOException e) {
			e.printStackTrace();
		}
	}
	
	public static void wxQRCode(WechatConfig wechatConfig,String backetName,String code,String shopId,String path,String name,String url){
		AccessToken accessToken = getAccessToken(wechatConfig);
		Map<String, Object> params = new HashMap<>();
		/*if(StringUtils.isNoneBlank(code)){
			params.put("scene", code);//参数
			params.put("scene", shopId);
		}*/
		params.put("path", url+"?shopId="+shopId);//路径
		params.put("width", 430);
		CloseableHttpClient  httpClient = HttpClientBuilder.create().build();
        HttpPost httpPost = new HttpPost(Constants.URL_QRCODE_CREATE+accessToken.getAccessToken());  // 接口
        httpPost.addHeader(HTTP.CONTENT_TYPE, "application/json");
        String body = JsonUtil.toJson(params); //必须是json模式的 post   
        StringEntity entity;
        try {
			entity = new StringEntity(body);
			entity.setContentType("image/png");
	        httpPost.setEntity(entity);
	        HttpResponse response;

	        response = httpClient.execute(httpPost);
	        InputStream inputStream = response.getEntity().getContent();
	        //saveToImgByInputStream(inputStream,path,name);  //保存图片
	        AliyunOSSClientUtil.uploadObject2OSS(inputStream, name, backetName, path);
		} catch (UnsupportedEncodingException e) {
			e.printStackTrace();
		} catch (ClientProtocolException e) {
			e.printStackTrace();
		} catch (IOException e) {
			e.printStackTrace();
		}
	}
	
	public static void wxQRCode(WechatConfig wechatConfig,String backetName,String path,String name,String url,Map<String,String> param){
		AccessToken accessToken = getAccessToken(wechatConfig);
		Map<String, Object> params = new HashMap<String, Object>();
		
		StringBuffer paramStr=new StringBuffer();
		for(Entry<String, String> obj:param.entrySet()){
			paramStr.append(obj.getKey());
			paramStr.append("=");
			paramStr.append(obj.getValue());
			paramStr.append("&");
		}
		paramStr.substring(0, paramStr.length()-1);
		
		url=url+"?"+paramStr.toString();
		params.put("path", url);//路径
		params.put("width", 430);
		CloseableHttpClient  httpClient = HttpClientBuilder.create().build();
        HttpPost httpPost = new HttpPost(Constants.URL_QRCODE_CREATE+accessToken.getAccessToken());  // 接口
        httpPost.addHeader(HTTP.CONTENT_TYPE, "application/json");
        String body = JsonUtil.toJson(params); //必须是json模式的 post   
        StringEntity entity;
        try {
			entity = new StringEntity(body);
			entity.setContentType("image/png");
	        httpPost.setEntity(entity);
	        HttpResponse response;

	        response = httpClient.execute(httpPost);
	        InputStream inputStream = response.getEntity().getContent();
	        AliyunOSSClientUtil.uploadObject2OSS(inputStream, name, backetName, path);
		} catch (UnsupportedEncodingException e) {
			e.printStackTrace();
		} catch (ClientProtocolException e) {
			e.printStackTrace();
		} catch (IOException e) {
			e.printStackTrace();
		}
	}
	
	
	public static int saveToImgByInputStream(InputStream instreams,String imgPath,String imgName){
        int stateInt = 1;
        if(instreams != null){
            try {
            	File fileFolder = new File(imgPath);
    			if (!fileFolder.exists()) {
    				fileFolder.mkdirs();
    			}
            	
                File file=new File(imgPath,imgName);//可以是任何图片格式.jpg,.png等
                FileOutputStream fos=new FileOutputStream(file);
                byte[] b = new byte[1024];
                int nRead = 0;
                while ((nRead = instreams.read(b)) != -1) {
                    fos.write(b, 0, nRead);
                }
                fos.flush();
                fos.close();                
            } catch (Exception e) {
                stateInt = 0;
                e.printStackTrace();
            } finally {
            }
        }
        return stateInt;
    }

	public static void main(String[] args) {
		System.out.println(getKeyFullName("memberId"));
	}

}
